ItIron2021
Javascript
作者發燒中,但文章還是得發?
昨天的主題是關於js如何傳遞值,了解基本型別與物件間的差別是極端重要的概念,今天馬上來一個應用題目吧!
請解釋在JS中你要如何拷貝一個物件或是一個陣列
又是一個看起來很簡單但一時間你可能回答不出來的題目對吧? 當碰到比較困難的題目時,我會先試著把題目做簡化,我們先縮小題目的範圍吧!
請解釋在JS中你要如何拷貝一個陣列
有了昨天的教訓,我們都很清楚下方的寫法是會有問題的
let arr1 = [1, 2, 3]
let arr2 = arr1
這兩個玩意有著相同的reference,你修改一個、另一個也會同樣受到影響,所以這很明顯是出局的,以下有幾個常見的方法!
沒錯,就是你熟悉的for loop,這樣最基礎的做法往往最容易被人忽略
let arr1 = [1, 2, 3]
let arr2 = []
for (let i = 0; i < arr1.length; i++) {
arr2.push(arr1[i])
}
同樣的道理,你當然也能用map或filter這樣的語法糖去複製一個陣列!
另一個切入的方向就是利用js原生的一些方法去做到直接複製的效果,上述的兩種方法也能很好的達成任務
let arr1 = [1, 2, 3]
let arr2 = [].concat(arr1)
let arr3 = arr1.slice()
arr2.push(4)
arr3.push(5)
console.log(arr1) // [1, 2, 3]
console.log(arr2) // [1, 2, 3, 4]
console.log(arr3) // [1, 2, 3, 5]
你可以看到三者確實已經是不同的reference了。
這玩意可說是我最喜歡的方法了,新加入的擴展運算子在許多地方都很實用,用來做陣列的複製與合併再適合不過了。
let arr1 = [1, 2, 3]
let arr2 = [...arr1] // [1, 2, 3]
這方法同樣也適用於物件的複製
let obj1 = {
name: 'Danny',
age: 30
}
let obj2 = {
...obj1
}
上述的幾種方法雖然都成功地複製了陣列/物件,但他們全都屬於所謂的淺拷貝(shallow clone),也就是說萬一你今天複製的對象裡面還有其他的物件,裡面的物件會還是保持同一個reference,我們看個簡單的例子。
let demo = [0, [1, 2]]
let demoClone = demo.slice() // [0, [1, 2]]
demoClone[1].push(3)
console.log(demoClone) // [0, [1, 2, 3]]
console.log(demo) // [0, [1, 2, 3]]
上述的範例中雖然成功的複製了一維度的陣列,但裡面的物件仍保有相同的reference,造成了你看到的結果,為了解決這樣的問題,你需要的是深拷貝(deep clone),而在js中最簡單做到深拷貝的辦法就是先轉成json字串再parse回來,原理大致上是因為字串屬於基本型別,所以複製的時候會重新建立一個記憶體位置,其他的細節老樣子自己去查?
let demo = [0, [1, 2]]
let demoClone = JSON.parse(JSON.stringify(demo))
demoClone[1].push(3)
console.log(demoClone) // [0, [1, 2, 3]]
console.log(demo) // [0, [1, 2]]
你可以看到,這麼一來連裡面的物件參照也完全不同囉! 但這個方法有個小缺陷,由於是要轉成json再轉成字串儲存,你原先的內容就要是合法的json數值,萬一裡面包含著函數就會直接掰掰了,這種時候你就要想辦法去處理深拷貝囉! 但處理面試只要能回答以上幾個方向基本上就完全沒有問題了!
物件與陣列的複製方式都很接近,也就是迴圈、型別既有語法以及擴展運算子,最終的大殺招就是轉json字串再轉回來做的深拷貝。
深拷貝 vs 淺拷貝、擴展運算子
本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!